#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
enum class STATUS
{
    NEW = 1,      // 新建,还未连接
    CONNECTING,   // 正在连接,过程状态
    CONNECTED,    // 连接或者重连成功
    DISCONNECTED, // 重连失败
    CLOSED        // 连接失败,经历重连仍然失败,关闭进程
};
#define RETRYINTERVAL 4
#define MAXRETRIES 4
static const int gdefaultsockfd = -1;
void Usage(const std::string &process)
{
    std::cout << "Usage:" << process << "server_ip server_port" << std::endl;
}

class ClientConnection
{
public:
    ClientConnection(std::string ip, uint16_t port)
        : _sockfd(gdefaultsockfd),
          _serverport(port), _serverip(ip), _retry_interval(RETRYINTERVAL), _max_retries(MAXRETRIES),
          _status(STATUS::NEW)
    {
    }
    void Connect()
    {
        // 1.创建套接字
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            std::cout << "socket filed" << std::endl;
            exit(1);
        }
        std::cout << "socket success,socket:"<<socket << std::endl;
        // 2.填充网络信息
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        server.sin_addr.s_addr = inet_addr(_serverip.c_str());
        // 3.不需要显式绑定
        // 4.进行连接
        int n = connect(_sockfd, (sockaddr *)&server, sizeof(server));
        if (n < 0)
        {
            Disconnect();                   // 恢复_sockfd的默认值,这里是连接失败,并不是创建套接字失败
            _status = STATUS::DISCONNECTED; // 连接失败
            return;
        }
        _status = STATUS::CONNECTED;
    }
    void Reconnect()
    {
        _status = STATUS::CONNECTING;
        int count = 0;
        while (count < _max_retries)
        {
            Connect(); // 进行重连
            if (_status == STATUS::CONNECTED)
            {
                return;
            }
            sleep(_retry_interval);
            count++;
            std::cout << "重连次数:" << count << "/最大重连次数:" << _max_retries << std::endl;
        }
        _status = STATUS::CLOSED; // 最大重连次数之后还没有连上就关闭
    }
    void Disconnect()
    {
        if (_sockfd != -1)
        {
            close(_sockfd);
            _status = STATUS::CLOSED;
            _sockfd = -1;
        }
    }
    void Process()
    {
        // 收发消息,简单IO
        while (true)
        {
            std::string message;
            char inbuffer[4096];
            std::cout << "Enter#";
            getline(std::cin, message);
            int n = send(_sockfd, message.c_str(), message.size(), 0);
            if (n > 0)
            {
                inbuffer[n] = 0;
                int m = recv(_sockfd, inbuffer, sizeof(inbuffer), 0);
                if (m > 0)
                {
                    std::cout << "Server#" << inbuffer << std::endl;
                }
                else if (m == 0) // 服务端掉线了
                {
                    _status = STATUS::DISCONNECTED;
                    break;
                }
                else
                {
                    std::cout << "read m:" << m << "error:" << errno << "error string:" << strerror(errno) << std::endl;
                    _status = STATUS::CLOSED;
                    break;
                }
            }
            else
            {
                std::cout << "write n:" << n << "error:" << errno << "error string:" << strerror(errno) << std::endl;
                _status = STATUS::CLOSED;
                break;
            }
        }
    }
    int GetSockfd()
    {
        return _sockfd;
    }
    STATUS GetStatus()
    {
        return _status;
    }
    ~ClientConnection()
    {
        Disconnect();
    }

private:
    int _sockfd;
    uint16_t _serverport;  // 服务端端口号
    std::string _serverip; // 服务端IP地址
    int _retry_interval;   // 重连间隔
    int _max_retries;      // 最大重试次数
    STATUS _status;        // 连接状态
};
class TcpClient
{
public:
    TcpClient(std::string ip, uint16_t port)
        : _connect(ip, port)
    {
    }
    void Excute()
    {
        while (true) // 根据不同的连接状态调用不同的连接方法
        {
            switch (_connect.GetStatus())
            {
            // 新建开始连接
            case STATUS::NEW:
            {
                _connect.Connect();
                break;
            }
            // 连接成功开始通信
            case STATUS::CONNECTED:
            {
                std::cout << "连接成功,进行通信" << std::endl;
                _connect.Process();
                break;
            }
            // 连接失败就重连
            case STATUS::DISCONNECTED:
            {
                std::cout << "连接失败或服务器掉线,进行重连" << std::endl;
                _connect.Reconnect();
                break;
            }
            // 关闭
            case STATUS::CLOSED:
            {
                _connect.Disconnect();
                std::cout << "重连失败,退出" << std::endl;
                return; // 退出
            }
            default:
            {
                break;
            }
            }
        }
    }
    ~TcpClient()
    {
    }

private:
    ClientConnection _connect;
};
int main(int args, char *argv[])
{
    if (args != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);
    TcpClient client(serverip, serverport);
    client.Excute();
    return 0;
}